﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;
using IPC.Communication.BasicFramework;
using IPC.Communication.Core;
using IPC.Communication.Core.Address;
using IPC.Communication.Core.IMessage;
using IPC.Communication.Core.Net;
using IPC.Communication.Reflection;

namespace IPC.Communication.Profinet.Fuji
{
    public class FujiSPHNet : NetworkDeviceBase
    {
        private byte _connectionID = 254;
        public byte ConnectionID
        {
            get { return _connectionID; }
            set { _connectionID = value; }
        }


        public FujiSPHNet()
        {
            base.ByteTransform = new RegularByteTransform();
            base.WordLength = 1;
        }

        public FujiSPHNet(string ipAddress, int port = 18245)
            : this()
        {
            IpAddress = ipAddress;
            Port = port;
        }

        protected override INetMessage GetNewNetMessage()
        {
            return new FujiSPHMessage();
        }

        private OperateResult<byte[]> ReadFujiSPHAddress(FujiSPHAddress address, ushort length)
        {
            OperateResult<List<byte[]>> operateResult = BuildReadCommand(ConnectionID, address, length);
            if (!operateResult.IsSuccess)
            {
                return operateResult.ConvertFailed<byte[]>();
            }
            List<byte> list = new List<byte>();
            for (int i = 0; i < operateResult.Content.Count; i++)
            {
                OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content[i]);
                if (!operateResult2.IsSuccess)
                {
                    return operateResult2;
                }
                OperateResult<byte[]> operateResult3 = ExtractActualData(operateResult2.Content);
                if (!operateResult3.IsSuccess)
                {
                    return operateResult3;
                }
                list.AddRange(operateResult3.Content);
            }
            return OperateResult.CreateSuccessResult(list.ToArray());
        }

        [HslMqttApi("ReadByteArray", "")]
        public override OperateResult<byte[]> Read(string address, ushort length)
        {
            OperateResult<FujiSPHAddress> operateResult = FujiSPHAddress.ParseFrom(address);
            if (!operateResult.IsSuccess)
            {
                return operateResult.ConvertFailed<byte[]>();
            }
            return ReadFujiSPHAddress(operateResult.Content, length);
        }

        [HslMqttApi("WriteByteArray", "")]
        public override OperateResult Write(string address, byte[] value)
        {
            OperateResult<byte[]> operateResult = BuildWriteCommand(ConnectionID, address, value);
            if (!operateResult.IsSuccess)
            {
                return operateResult.ConvertFailed<byte[]>();
            }
            OperateResult<byte[]> operateResult2 = ReadFromCoreServer(operateResult.Content);
            if (!operateResult2.IsSuccess)
            {
                return operateResult2;
            }
            OperateResult<byte[]> operateResult3 = ExtractActualData(operateResult2.Content);
            if (!operateResult3.IsSuccess)
            {
                return operateResult3;
            }
            return OperateResult.CreateSuccessResult();
        }

        [HslMqttApi("ReadBoolArray", "")]
        public override OperateResult<bool[]> ReadBool(string address, ushort length)
        {
            OperateResult<FujiSPHAddress> operateResult = FujiSPHAddress.ParseFrom(address);
            if (!operateResult.IsSuccess)
            {
                return operateResult.ConvertFailed<bool[]>();
            }
            int num = operateResult.Content.BitIndex + length;
            int num2 = (num % 16 == 0) ? (num / 16) : (num / 16 + 1);
            OperateResult<byte[]> operateResult2 = ReadFujiSPHAddress(operateResult.Content, (ushort)num2);
            if (!operateResult2.IsSuccess)
            {
                return operateResult2.ConvertFailed<bool[]>();
            }
            return OperateResult.CreateSuccessResult(operateResult2.Content.ToBoolArray().SelectMiddle(operateResult.Content.BitIndex, length));
        }

        [HslMqttApi("WriteBoolArray", "")]
        public override OperateResult Write(string address, bool[] value)
        {
            OperateResult<FujiSPHAddress> operateResult = FujiSPHAddress.ParseFrom(address);
            if (!operateResult.IsSuccess)
            {
                return operateResult.ConvertFailed<bool[]>();
            }
            int num = operateResult.Content.BitIndex + value.Length;
            int num2 = (num % 16 == 0) ? (num / 16) : (num / 16 + 1);
            OperateResult<byte[]> operateResult2 = ReadFujiSPHAddress(operateResult.Content, (ushort)num2);
            if (!operateResult2.IsSuccess)
            {
                return operateResult2.ConvertFailed<bool[]>();
            }
            bool[] array = operateResult2.Content.ToBoolArray();
            value.CopyTo(array, operateResult.Content.BitIndex);
            OperateResult<byte[]> operateResult3 = BuildWriteCommand(ConnectionID, address, array.ToByteArray());
            if (!operateResult3.IsSuccess)
            {
                return operateResult3.ConvertFailed<byte[]>();
            }
            OperateResult<byte[]> operateResult4 = ReadFromCoreServer(operateResult3.Content);
            if (!operateResult4.IsSuccess)
            {
                return operateResult4;
            }
            OperateResult<byte[]> operateResult5 = ExtractActualData(operateResult4.Content);
            if (!operateResult5.IsSuccess)
            {
                return operateResult5;
            }
            return OperateResult.CreateSuccessResult();
        }

        //[AsyncStateMachine(typeof(<ReadFujiSPHAddressAsync>d__12))]
        //[DebuggerStepThrough]
        //private Task<OperateResult<byte[]>> ReadFujiSPHAddressAsync(FujiSPHAddress address, ushort length)
        //{
        //    <ReadFujiSPHAddressAsync>d__12 stateMachine = new <ReadFujiSPHAddressAsync>d__12();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<byte[]>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.length = length;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<ReadAsync>d__13))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult<byte[]>> ReadAsync(string address, ushort length)
        //{
        //    <ReadAsync>d__13 stateMachine = new <ReadAsync>d__13();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<byte[]>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.length = length;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<WriteAsync>d__14))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult> WriteAsync(string address, byte[] value)
        //{
        //    <WriteAsync>d__14 stateMachine = new <WriteAsync>d__14();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.value = value;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<ReadBoolAsync>d__15))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult<bool[]>> ReadBoolAsync(string address, ushort length)
        //{
        //    <ReadBoolAsync>d__15 stateMachine = new <ReadBoolAsync>d__15();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<bool[]>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.length = length;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<WriteAsync>d__16))]
        //[DebuggerStepThrough]
        //public override Task<OperateResult> WriteAsync(string address, bool[] value)
        //{
        //    <WriteAsync>d__16 stateMachine = new <WriteAsync>d__16();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.address = address;
        //    stateMachine.value = value;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        [HslMqttApi]
        public OperateResult CpuBatchStart()
        {
            return ReadFromCoreServer(PackCommand(ConnectionID, 4, 0, null)).Check((Func<byte[], OperateResult>)ExtractActualData);
        }

        [HslMqttApi]
        public OperateResult CpuBatchInitializeAndStart()
        {
            return ReadFromCoreServer(PackCommand(ConnectionID, 4, 1, null)).Check((Func<byte[], OperateResult>)ExtractActualData);
        }

        [HslMqttApi]
        public OperateResult CpuBatchStop()
        {
            return ReadFromCoreServer(PackCommand(ConnectionID, 4, 2, null)).Check((Func<byte[], OperateResult>)ExtractActualData);
        }

        [HslMqttApi]
        public OperateResult CpuBatchReset()
        {
            return ReadFromCoreServer(PackCommand(ConnectionID, 4, 3, null)).Check((Func<byte[], OperateResult>)ExtractActualData);
        }

        [HslMqttApi]
        public OperateResult CpuIndividualStart()
        {
            return ReadFromCoreServer(PackCommand(ConnectionID, 4, 4, null)).Check((Func<byte[], OperateResult>)ExtractActualData);
        }

        [HslMqttApi]
        public OperateResult CpuIndividualInitializeAndStart()
        {
            return ReadFromCoreServer(PackCommand(ConnectionID, 4, 5, null)).Check((Func<byte[], OperateResult>)ExtractActualData);
        }

        [HslMqttApi]
        public OperateResult CpuIndividualStop() { 
            return ReadFromCoreServer(PackCommand(ConnectionID, 4, 6, null)).Check((Func<byte[], OperateResult>)ExtractActualData);
        }

        [HslMqttApi]
        public OperateResult CpuIndividualReset()
        {
            return ReadFromCoreServer(PackCommand(ConnectionID, 4, 7, null)).Check((Func<byte[], OperateResult>)ExtractActualData);
        }

        //[AsyncStateMachine(typeof(<CpuBatchStartAsync>d__25))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> CpuBatchStartAsync()
        //{
        //    <CpuBatchStartAsync>d__25 stateMachine = new <CpuBatchStartAsync>d__25();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<CpuBatchInitializeAndStartAsync>d__26))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> CpuBatchInitializeAndStartAsync()
        //{
        //    <CpuBatchInitializeAndStartAsync>d__26 stateMachine = new <CpuBatchInitializeAndStartAsync>d__26();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<CpuBatchStopAsync>d__27))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> CpuBatchStopAsync()
        //{
        //    <CpuBatchStopAsync>d__27 stateMachine = new <CpuBatchStopAsync>d__27();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<CpuBatchResetAsync>d__28))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> CpuBatchResetAsync()
        //{
        //    <CpuBatchResetAsync>d__28 stateMachine = new <CpuBatchResetAsync>d__28();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<CpuIndividualStartAsync>d__29))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> CpuIndividualStartAsync()
        //{
        //    <CpuIndividualStartAsync>d__29 stateMachine = new <CpuIndividualStartAsync>d__29();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<CpuIndividualInitializeAndStartAsync>d__30))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> CpuIndividualInitializeAndStartAsync()
        //{
        //    <CpuIndividualInitializeAndStartAsync>d__30 stateMachine = new <CpuIndividualInitializeAndStartAsync>d__30();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<CpuIndividualStopAsync>d__31))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> CpuIndividualStopAsync()
        //{
        //    <CpuIndividualStopAsync>d__31 stateMachine = new <CpuIndividualStopAsync>d__31();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        //[AsyncStateMachine(typeof(<CpuIndividualResetAsync>d__32))]
        //[DebuggerStepThrough]
        //public Task<OperateResult> CpuIndividualResetAsync()
        //{
        //    <CpuIndividualResetAsync>d__32 stateMachine = new <CpuIndividualResetAsync>d__32();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        public override string ToString()
        {
            return String.Format("FujiSPHNet[{0}:{1}]", IpAddress, Port);
        }

        public static string GetErrorDescription(byte code)
        {
            switch (code)
            {
                case 16:
                    return "Command cannot be executed because an error occurred in the CPU.";
                case 17:
                    return "Command cannot be executed because the CPU is running.";
                case 18:
                    return "Command cannot be executed due to the key switch condition of the CPU.";
                case 32:
                    return "CPU received undefined command or mode.";
                case 34:
                    return "Setting error was found in command header part.";
                case 35:
                    return "Transmission is interlocked by a command from another device.";
                case 40:
                    return "Requested command cannot be executed because another command is now being executed.";
                case 43:
                    return "Requested command cannot be executed because the loader is now performing another processing( including program change).";
                case 47:
                    return "Requested command cannot be executed because the system is now being initialized.";
                case 64:
                    return "Invalid data type or number was specified.";
                case 65:
                    return "Specified data cannot be found.";
                case 68:
                    return "Specified address exceeds the valid range.";
                case 69:
                    return "Address + the number of read/write words exceed the valid range.";
                case 160:
                    return "No module exists at specified destination station No.";
                case 162:
                    return "No response data is returned from the destination module.";
                case 164:
                    return "Command cannot be communicated because an error occurred in the SX bus.";
                case 165:
                    return "Command cannot be communicated because NAK occurred while sending data via the SX bus.";
                default:
                    return StringResources.Language.UnknownError;
            }
        }

        private static byte[] PackCommand(byte connectionId, byte command, byte mode, byte[] data)
        {
            if (data == null)
            {
                data = new byte[0];
            }
            byte[] array = new byte[20 + data.Length];
            array[0] = 251;
            array[1] = 128;
            array[2] = 128;
            array[3] = 0;
            array[4] = byte.MaxValue;
            array[5] = 123;
            array[6] = connectionId;
            array[7] = 0;
            array[8] = 17;
            array[9] = 0;
            array[10] = 0;
            array[11] = 0;
            array[12] = 0;
            array[13] = 0;
            array[14] = command;
            array[15] = mode;
            array[16] = 0;
            array[17] = 1;
            array[18] = BitConverter.GetBytes(data.Length)[0];
            array[19] = BitConverter.GetBytes(data.Length)[1];
            if (data.Length != 0)
            {
                data.CopyTo(array, 20);
            }
            return array;
        }

        public static OperateResult<List<byte[]>> BuildReadCommand(byte connectionId, string address, ushort length)
        {
            OperateResult<FujiSPHAddress> operateResult = FujiSPHAddress.ParseFrom(address);
            if (!operateResult.IsSuccess)
            {
                return operateResult.ConvertFailed<List<byte[]>>();
            }
            return BuildReadCommand(connectionId, operateResult.Content, length);
        }

        public static OperateResult<List<byte[]>> BuildReadCommand(byte connectionId, FujiSPHAddress address, ushort length)
        {
            List<byte[]> list = new List<byte[]>();
            int[] array = SoftBasic.SplitIntegerToArray(length, 230);
            for (int i = 0; i < array.Length; i++)
            {
                list.Add(PackCommand(connectionId, 0, 0, new byte[6]
			{
				address.TypeCode,
				BitConverter.GetBytes(address.AddressStart)[0],
				BitConverter.GetBytes(address.AddressStart)[1],
				BitConverter.GetBytes(address.AddressStart)[2],
				BitConverter.GetBytes(array[i])[0],
				BitConverter.GetBytes(array[i])[1]
			}));
                address.AddressStart += array[i];
            }
            return OperateResult.CreateSuccessResult(list);
        }

        public static OperateResult<byte[]> BuildWriteCommand(byte connectionId, string address, byte[] data)
        {
            OperateResult<FujiSPHAddress> operateResult = FujiSPHAddress.ParseFrom(address);
            if (!operateResult.IsSuccess)
            {
                return operateResult.ConvertFailed<byte[]>();
            }
            int value = data.Length / 2;
            byte[] array = new byte[6 + data.Length];
            array[0] = operateResult.Content.TypeCode;
            array[1] = BitConverter.GetBytes(operateResult.Content.AddressStart)[0];
            array[2] = BitConverter.GetBytes(operateResult.Content.AddressStart)[1];
            array[3] = BitConverter.GetBytes(operateResult.Content.AddressStart)[2];
            array[4] = BitConverter.GetBytes(value)[0];
            array[5] = BitConverter.GetBytes(value)[1];
            data.CopyTo(array, 6);
            return OperateResult.CreateSuccessResult(PackCommand(connectionId, 1, 0, array));
        }

        public static OperateResult<byte[]> ExtractActualData(byte[] response)
        {
            try
            {
                if (response[4] != 0)
                {
                    return new OperateResult<byte[]>(response[4], GetErrorDescription(response[4]));
                }
                if (response.Length > 26)
                {
                    return OperateResult.CreateSuccessResult(response.RemoveBegin(26));
                }
                return OperateResult.CreateSuccessResult(new byte[0]);
            }
            catch (Exception ex)
            {
                return new OperateResult<byte[]>(ex.Message + " Source: " + response.ToHexString(' '));
            }
        }
    }
}